第56章 面向对象编程
面向对象编程(Object-Oriented Programming,简称OOP)是一种以"对象"为核心的编程范式,通过封装、继承、多态等特性,实现代码的模块化、复用性和可维护性。C++是支持面向对象编程的典型语言,其在C语言的基础上引入了类、对象、继承等概念,为复杂程序的设计提供了强大工具。
56.1 面向对象编程的基本概念
56.1.1 对象
对象是现实世界中实体的抽象,由属性(数据)和行为(操作数据的方法)组成。例如,"学生"对象的属性包括姓名、学号、成绩等,行为包括选课、考试等。在程序中,对象是类的实例,一个类可以创建多个对象。
56.1.2 类
类是对具有相同属性和行为的对象的抽象描述,是创建对象的模板。类定义了对象的属性(成员变量)和行为(成员函数),但不占用实际内存空间;对象是类的实例化,占用内存空间。例如,"学生类"定义了所有学生共有的属性和行为,而具体的学生(如"张三""李四")则是该类的对象。
56.1.3 面向对象的三大特性
- 封装(Encapsulation):将对象的属性和行为结合为一个整体,隐藏内部实现细节,仅通过公共接口与外部交互。
- 继承(Inheritance):子类可以继承父类的属性和行为,并可以添加新的属性和行为或重写父类的行为,实现代码复用和扩展。
- 多态(Polymorphism):同一操作作用于不同对象时,产生不同的执行结果。多态分为编译时多态(函数重载)和运行时多态(虚函数)。
56.2 类与对象的定义和使用
56.2.1 类的定义
#include <string>
using namespace std;
//学生类定义
class Student{
private:
//私有成员:仅类内部可访问
string name;
public:
//公共成员:类外部可访问(接口)
int id;
float score;
//构造函数:初始化对象
Student(string n,int i,float s){
name = n;
id = i;
score = s;
}
//成员函数:设置成绩(行为)
void setScore(float s){
if(s>=0&&s<=100){
score = s;
}
}
//成员函数:获取成绩(行为)
float getScore(){
return score;
}
//成员函数:显示信息(行为)
void showInfo(){
cout << "姓名:" << name << ",学号:" << id << ",成绩:" << score << endl;
}
};
56.2.2 访问控制符
C++通过访问控制符控制类成员的访问权限:
- private(私有):仅类内部可访问,隐藏实现细节。
- public(公共):类外部可直接访问,是类与外部交互的接口。
- protected(保护):类内部和子类可访问,用于继承场景。
56.2.3 对象的创建与使用
int main(){
//创建对象(实例化类)
Student stu1("张三", 1001, 90.5);
Student stu2("李四",1002, 85.0);
//调用成员函数(通过公共接口操作对象)
stu1.showInfo();
stu2.setScore(92.5);
stu2.showInfo();
return 0;
}
56.2.4 构造函数与析构函数
构造函数
与类名同名,无返回值,可重载,用于对象初始化。
class Person{
private:
string name;
int age;
public:
//无参构造函数
Person(){
name="未知";
age=0;
}
//带参构造函数
Person(string n,int a){
name = n;
age = a;
}
};
析构函数
类名前加~,无参数无返回值,不能重载,对象销毁时自动调用,用于释放资源。
class FileHandler {
private:
FILE* file;
public:
//构造函数:打开文件
FileHandler(string filename) {
file = fopen(filename.c_str(), "r");
}
//析构函数:释放文件资源
~FileHandler(){
if(file!= NULL){
fclose(file);
}
}
};
56.3 封装
56.3.1 封装实现逻辑
- 将成员变量设为private,禁止外部直接修改;
- 提供public成员函数作为读写接口,内部增加数据校验逻辑。
class BankAccount{
private:
double balance;//余额私有,外部不可直接修改
string accountId;
public:
BankAccount(string id, double initial) {
accountId = id;
if (initial>=0){
balance = initial;
}else{
balance=0;
}
}
//存款接口
void deposit(double amount) {
if (amount>0){
balance += amount;
}
}
//取款接口
bool withdraw(double amount) {
if (amount>0 && amount<= balance){
balance -= amount;
return true;
}
return false;
}
//查询余额接口
double getBalance(){
return balance;
}
};
56.3.2 封装优势
- 数据安全:限制外部随意修改,保证数据合法;
- 低耦合:内部逻辑修改时,外部调用代码无需改动;
- 功能集中:同一类的相关数据、操作统一管理。
56.4 继承
56.4.1 基础语法
格式:class 子类名 : 继承方式 父类名
继承方式:public、protected、private(默认private)
//父类
class Person {
protected:
string name;
int age;
public:
Person(string n, int a) : name (n), age (a) {}
void showBaseInfo(){
cout << "姓名:" << name << ",年龄:" << age << endl;
}
};
//子类 Student 公有继承 Person
class Student : public Person {
private:
int studentId;
public:
//子类构造函数,调用父类构造初始化
Student(string n, int a, int id) : Person(n, a), studentId(id) {}
void showStudentInfo(){
showBaseInfo();
cout << "学号:" << studentId << endl;
}
};
56.4.2 继承权限对照表
| 父成员 | public继承 | protected继承 | private继承 |
|---|---|---|---|
| public | public | protected | private |
| protected | protected | protected | private |
| private | 子类不可访问 | 子类不可访问 | 子类不可访问 |
56.4.3 方法重写(Override)
父类声明virtual虚函数,子类定义同名同参函数完成重写,运行时多态基础。
class Animal {
public:
virtual void makeSound(){
cout<<"动物发出声音"<<endl;
}
};
class Dog : public Animal {
public:
void makeSound() override{
cout<<"汪汪汪"<<endl;
}
};
class Cat : public Animal {
public:
void makeSound() override{
cout<<"喵喵喵"<<endl;
}
};
56.4.4 继承优势
- 代码复用:直接复用父类已有属性与函数;
- 层次化设计:清晰描述"is-a"关系;
- 可扩展:新增功能仅写子类,不改动原有代码。
56.5 多态
56.5.1 编译时多态(函数重载)
同一类中同名函数,参数数量/类型不同,编译区分调用。
class Calculator {
public:
int add(int a,int b){
return a+b;
}
double add(double a,double b){
return a+b;
}
int add(int a,int b,int c){
return a+b+c;
}
};
int main(){
Calculator calc;
calc.add(1, 2);
calc.add(1.5, 2.5);
calc.add(1, 2, 3);
return 0;
}
56.5.2 运行时多态(虚函数)
父类指针/引用指向子类对象,自动执行子类重写的虚函数。
int main(){
Animal* animal1 =new Dog();
Animal* animal2 = new Cat;
animal1->makeSound();
animal2->makeSound();
delete animal1;
delete animal2;
return 0;
}
实现条件:
- 父类函数加virtual;
- 子类同名重写;
- 使用父类指针/引用操作子类实例。
56.5.3 多态优势
统一接口适配多种实现,新增子类无需修改上层调用代码,符合开闭原则。
56.6 其他重要概念
56.6.1 抽象类与纯虚函数
包含纯虚函数virtual 函数() = 0的类为抽象类,无法实例化,强制子类实现全部纯虚函数。
//抽象类
class Shape {
public:
virtual double getArea() = 0;
virtual double getPerimeter() = 0;
};
//子类必须实现两个纯虚函数
class Circle : public Shape {
private:
double radius;
public:
Circle (double r): radius(r) {}
double getArea() override {
return 3.14 * radius * radius;
}
double getPerimeter() override {
return 2* 3.14* radius;
}
};
56.6.2 友元
friend关键字允许外部函数/类访问当前类私有成员,破坏封装,仅特殊场景使用。
class Point {
private:
int x, y;
public:
Point(int x, int y):x(x),y(y) {}
//声明友元函数
friend void printPoint (Point p);
};
//友元可访问私有x、y
void printPoint (Point p){
cout << "(" << p.x << "," << p.y <<")"<< endl;
}
56.63 this指针
成员函数隐含this指针,指向当前调用对象,区分局部变量与成员变量。
class Person {
private:
string name;
public:
void setName(string name) {
this->name=name;
}
};
56.7 面向对象编程的优势
- 模块化:每个类独立负责一类功能,团队协作更清晰;
- 复用性:继承、类实例化实现代码复用;
- 可维护:封装隔离内部实现,修改不影响外部;
- 可扩展:多态、继承便于新增业务逻辑。